#pragma once

namespace lfq
{
	namespace detail
	{

		template <typename T> class tls_data
		{
		public:
			tls_data()
				: m_tlsIndex(::TlsAlloc()), m_entries(nullptr)
			{
			}

			~tls_data()
			{
				delete_all_tls_data();

				::TlsFree(m_tlsIndex);
			}

			T & get_data()
			{
				void * ptr = ::TlsGetValue(m_tlsIndex);
				if (ptr == nullptr)
				{
					ptr = new_tls_entry();
					::TlsSetValue(m_tlsIndex, ptr);
				}

				return reinterpret_cast<Entry *>(ptr)->m_value;
			}

		private:
			DWORD const m_tlsIndex;

			struct Entry
			{
				T m_value;
				Entry * volatile m_next;
			};

			Entry * volatile m_entries;

		private:
			void * new_tls_entry()
			{
				Entry * ptr = new Entry();
				::TlsSetValue(m_tlsIndex, ptr);

				while (true)
				{
					Entry * oldTop = m_entries;
					ptr->m_next = oldTop;

					if (::InterlockedCompareExchangePointer((PVOID volatile *)&m_entries, ptr, oldTop) == oldTop)
					{
						break;
					}
				}
				return ptr;
			}

			void delete_all_tls_data()
			{
				Entry * top = (Entry *)InterlockedExchangePointer((PVOID volatile *)&m_entries, nullptr);

				while (top != nullptr)
				{
					Entry * cur = top;
					top = top->m_next;

					delete cur;
				}
			}
		};

	}
}
